﻿#include "AsOpenGLWidget.h"

AsOpenGLWidget::AsOpenGLWidget(QWidget *parent)
	:QOpenGLWidget(nullptr)
	, m_vbo(QOpenGLBuffer::VertexBuffer)
{}

AsOpenGLWidget::~AsOpenGLWidget()
{
	clearup();
}

void AsOpenGLWidget::setVertexArray(QVector<QVector3D> points)
{
	createColorAndNormal(points);
	createLODModels();
}

void AsOpenGLWidget::setXTranslate(float distance)
{
	if (distance != m_xTrans)
	{
		m_xTrans = distance;
		update();
	}
}

void AsOpenGLWidget::setYTranslate(float distance)
{
	if (distance != m_yTrans)
	{
		m_yTrans = distance;
		update();
	}
}

void AsOpenGLWidget::clearup()
{
	if (m_program == nullptr)
	{
		return;
	}
	makeCurrent();
	m_vbo.destroy();
	delete m_program;
	m_program = nullptr;
	doneCurrent();
}

void AsOpenGLWidget::initializeGL()
{
	setVertexArray(PtrCoreData->getVec3DMap().value(0));
	connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &AsOpenGLWidget::clearup);
	initializeOpenGLFunctions();
	glClearColor(0.7, 0.7, 0.7, 1);
	m_program = new QOpenGLShaderProgram;
	m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "D:/gitLab/AiString/NewPCIDemo/jni/src/NewPCIDemoApplication/shader/openglWidget.vert");
	m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "D:/gitLab/AiString/NewPCIDemo/jni/src/NewPCIDemoApplication/shader/openglWidget.frag");
	m_program->link();
	m_program->bind();
	m_projLoc = m_program->uniformLocation("projMatrix");
	m_mvLoc = m_program->uniformLocation("mvMatrix");
	m_normalLoc = m_program->uniformLocation("normalMatrix");
	m_plane = m_program->uniformLocation("plane");
	m_lightPos = m_program->uniformLocation("lightPos");

	//updateModel();

	m_camera.setToIdentity();
	m_camera.translate(0, 0, -3);

	m_program->setUniformValue(m_lightPos, QVector3D(0, 0, 30));
	m_program->release();
}

void AsOpenGLWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);

	updateModel();

	m_camera.setToIdentity();
	m_camera.translate(m_xTrans, -m_yTrans, m_zoomCoe);
	m_camera.rotate(m_yRot + m_tryRotY, 0, 1, 0);
	m_camera.rotate(m_xRot + m_tryRotX, 1, 0, 0);

	m_world.setToIdentity();
	/*m_world.rotate(m_yRot + m_tryRotY, 0, 1, 0);
	m_world.rotate(m_xRot + m_tryRotX, 1, 0, 0);*/
	m_world.translate(0, -1, 0);
	m_world.rotate(-90, 1, 0, 0);
	m_world.scale(1, -1, 1);

	updateTransformParam();

	AsGLCoordPainter coord;
	coord.setTransformParam(m_transformParam);
	coord.renderCoord();

	QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
	m_program->bind();
	m_program->setUniformValue(m_projLoc, m_projMat);
	m_program->setUniformValue(m_mvLoc, m_camera * m_world);
	QMatrix3x3 normal = m_world.normalMatrix();
	m_program->setUniformValue(m_normalLoc, normal);
	m_program->setUniformValue(m_plane, QVector4D{ 0.0, 0.0, 1.0, -0.77 });
	//glDrawArrays(GL_TRIANGLES, 0, m_data.size() / 9);
	switch (m_distance)
	{
	case 0:
		glDrawArrays(GL_POINTS, 0, m_data.size() / 9);
		break;
	case 1:
		glDrawArrays(GL_POINTS, 0, m_LODMData.size() / 9);
		break;
	case 2:
		glDrawArrays(GL_POINTS, 0, m_LODSData.size() / 9);
		break;
	default:
		break;
	}
	//glDrawArrays(GL_POINTS, 0, m_LODMData.size() / 9);

	m_program->release();

	AsGLXYZPainter xyz;
	xyz.setTransformParam(m_transformParam);
	xyz.render();
}

void AsOpenGLWidget::resizeGL(int w, int h)
{
	m_projMat.setToIdentity();
	m_projMat.perspective(45.f, GLfloat(w) / h, 0.1f, 100.f);
}

void AsOpenGLWidget::timerEvent(QTimerEvent *e)
{
	update();
}

void AsOpenGLWidget::mousePressEvent(QMouseEvent *e)
{
	m_lastPos = e->pos();
	if (e->button() == Qt::RightButton)
	{
		m_tryRotX = 0;
		m_tryRotY = 0;
	}
	update();
}

void AsOpenGLWidget::mouseMoveEvent(QMouseEvent *e)
{
	int dx = e->x() - m_lastPos.x();
	int dy = e->y() - m_lastPos.y();
	float rotate_k = 0.3;
	float translate_k = 0.003;

	if (e->buttons() & Qt::LeftButton) {
		setXTranslate(m_xTrans + translate_k * dx);
		setYTranslate(m_yTrans + translate_k * dy);
	}
	else if (e->buttons() & Qt::RightButton) {
		m_tryRotX += rotate_k * dy;
		m_tryRotY += rotate_k * dx;
	}
	m_lastPos = e->pos();
	update();
}

void AsOpenGLWidget::mouseReleaseEvent(QMouseEvent *e)
{
	if (e->button() == Qt::RightButton) {
		m_xRot += m_tryRotX;
		m_yRot += m_tryRotY;
		m_tryRotX = 0;
		m_tryRotY = 0;
	}
	update();
}

void AsOpenGLWidget::wheelEvent(QWheelEvent *e)
{
	m_zoomCoe += (float)e->delta() / 1200;
	m_zoomCoe = qMax(-7.f, qMin(m_zoomCoe, 1.f));
	if (m_zoomCoe >= -5.1 && m_zoomCoe <= -5)
	{
		(m_distance <= 1) ? m_distance = 2 : m_distance = 1;
		updateModel();
	}
	else if (m_zoomCoe >= -2 && m_zoomCoe <= -1.9)
	{
		(m_distance >= 1) ? m_distance = 0 : m_distance = 1;
		updateModel();
	}
	update();
}

void AsOpenGLWidget::updateModel()
{
	m_vao.create();
	QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);

	m_vbo.create();
	m_vbo.bind();
	switch (m_distance)
	{
	case 0:
		m_vbo.allocate(m_data.constData(), m_data.size() * sizeof(GLfloat));
		break;
	case 1:
		m_vbo.allocate(m_LODMData.constData(), m_LODMData.size() * sizeof(GLfloat));
		break;
	case 2:
		m_vbo.allocate(m_LODSData.constData(), m_LODSData.size() * sizeof(GLfloat));
		break;
	default:
		break;
	}

	setupVertexAttribs();
}

void AsOpenGLWidget::updateTransformParam()
{
	m_transformParam.xRot = m_xRot + m_tryRotX;
	m_transformParam.yRot = m_yRot + m_tryRotY;
	m_transformParam.xTrans = m_xTrans;
	m_transformParam.yTrans = m_yTrans;
	m_transformParam.zoomCoe = m_zoomCoe;
	m_transformParam.projMatrix = m_projMat;
	m_transformParam.modelMatrix = m_world;
	m_transformParam.viewMatrix = m_camera;
}

void AsOpenGLWidget::setupVertexAttribs()
{
	int step = 9;
	m_vbo.bind();
	QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
	functions->glEnableVertexAttribArray(0);
	functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, step * sizeof(GLfloat), 0);
	functions->glEnableVertexAttribArray(1);
	functions->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, step * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
	functions->glEnableVertexAttribArray(2);
	functions->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, step * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));
	m_vbo.release();
}

void AsOpenGLWidget::createLODModels()
{
	int k = 20;
	int step = 9 * k;
	int size = 9;
	int num = 0;
	QVector<GLfloat>::iterator it;
	for (it = m_data.begin(); num < m_data.size() / (size + step) - 1; it += step)
	{
		for (int i = 0; i < size; i++)
		{
			m_LODMData.push_back(*it);
			it++;
		}
		num++;
	}

	num = 0;
	step = 90 * k;
	for (it = m_data.begin(); num < m_data.size() / (size + step) - 1; it += step)
	{
		for (int i = 0; i < size; i++)
		{
			m_LODSData.push_back(*it);
			it++;
		}
		num++;
	}
}

void AsOpenGLWidget::createColorAndNormal(QVector<QVector3D> input)
{
	m_data.resize(input.size() * 3 * 3);
	QVector<QVector3D>::iterator model_it;
	//
	QVector<float>::iterator data_it;
	float* ptrVbo=m_data.data();
	data_it = m_data.begin();
	float red = m_modelColor.redF();
	float* ptr = (float*)input.data();
	for (auto pt:input)
	{
		*ptrVbo++ = *ptr++ / 500.f -1;
		*ptrVbo++ = *ptr++ / 1000.f;
		*ptrVbo++ = *ptr++ / 100.f +0.5;
		*ptrVbo++ = m_modelColor.redF();
		*ptrVbo++ = m_modelColor.greenF();
		*ptrVbo++ = m_modelColor.blueF();
		*ptrVbo++ = 0;
		*ptrVbo++ = 0;
		*ptrVbo++ = 1;
	}
}
